﻿using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using GdNet.Core.Assistants;
using GdNet.Integrations.NHibernate.Mapping;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Context;
using NHibernate.Mapping.ByCode;

namespace GdNet.Integrations.NHibernate.SessionManagement
{
    public class NHibernateSessionManagerBuilder
    {
        private string hibernateConfiguration;
        private bool isWebApplication;
        private IList<IInterceptor> interceptors = new List<IInterceptor>();
        private IList<string> mappingFiles = new List<string>();

        private NHibernateSessionManagerBuilder()
        {
        }

        public static NHibernateSessionManagerBuilder NewInstance()
        {
            return new NHibernateSessionManagerBuilder();
        }

        public NHibernateSessionManagerBuilder UseConfigurationFile(string configurationFile)
        {
            this.hibernateConfiguration = configurationFile;
            return this;
        }

        public NHibernateSessionManagerBuilder IsWeb(bool value)
        {
            this.isWebApplication = value;
            return this;
        }

        public NHibernateSessionManagerBuilder AddInterceptor(IInterceptor interceptor)
        {
            this.interceptors.Add(interceptor);
            return this;
        }

        public NHibernateSessionManagerBuilder WithMappingFile(string mappingAssemblyFileName)
        {
            if (!File.Exists(mappingAssemblyFileName))
            {
                throw new FileNotFoundException(string.Format("The file {0} was not found", mappingAssemblyFileName));
            }
            this.mappingFiles.Add(mappingAssemblyFileName);
            return this;
        }

        /// <param name="mappingAssemblyFileNames">A list of mapping assemblies which separated by ';'</param>
        public NHibernateSessionManagerBuilder WithMappingFiles(string mappingAssemblyFileNames)
        {
            mappingAssemblyFileNames.Split(';').ToList().ForEach(x => this.WithMappingFile(x));
            return this;
        }

        public INHibernateSessionManager BuildSessionManager()
        {
            var nhConfiguration = new Configuration();
            if (File.Exists(this.hibernateConfiguration))
            {
                nhConfiguration.Configure(this.hibernateConfiguration);
            }

            this.interceptors.ToList().ForEach(interceptor => nhConfiguration.SetInterceptor(interceptor));

            var mapper = new ModelMapper();
            this.mappingFiles.ToList().ForEach(mappingAssemblyFile =>
            {
                var asm = Assembly.LoadFile(mappingAssemblyFile);
                var listeMappingTypes = ReflectionAssistant.GetTypesImplementedInterfaceOnAssembly(typeof(IEntityMapping), asm);
                mapper.AddMappings(listeMappingTypes);
            });
            nhConfiguration.AddDeserializedMapping(mapper.CompileMappingForAllExplicitlyAddedEntities(), string.Empty);

            ISessionFactory sessionFactory;
            if (this.isWebApplication)
            {
                sessionFactory = nhConfiguration.CurrentSessionContext<WebSessionContext>().BuildSessionFactory();
            }
            else
            {
                sessionFactory = nhConfiguration.CurrentSessionContext<CallSessionContext>().BuildSessionFactory();
            }

            return new NHibernateSessionManager(sessionFactory);
        }
    }
}
